﻿using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical; // 需要 Space, HVACLoadSpaceType
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using RevitProject.Helper; // 需要 RevitContextHelper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using Autodesk.Revit.DB.Analysis; // 需要 MessageBox
using System.IO; // 需要 IO
using Newtonsoft.Json; // 需要 Json.NET
using RevitProject.SpaceManager.Configuration;
using System.Windows.Threading;
using RevitProject.SpaceManager.Core;

namespace RevitProject.SpaceManager.ViewModel.SpaceViewModel
{
    public partial class SpaceTabViewModel : ObservableObject
    {
        private readonly Document _doc;
        private readonly UIDocument _uiDoc;
        private readonly UIApplication _uiApp;
        private List<Space> _allSpaces = new List<Space>(); // 存储所有 Space 对象
        private static readonly object AllLevelsPlaceholder = SpaceManagerVm.AllLevelsPlaceholder; // 复用占位符
        private readonly string _configFilePath;
        internal SpaceTypeTreeNodeViewModel _dropTargetNode = null;
        internal List<SpaceDisplayViewModel> _droppedSpaceVMs = null;


        // --- 属性 ---
        private List<object> _levels;
        public List<object> Levels { get => _levels; private set => SetProperty(ref _levels, value); }

        private object _selectedLevel;
        public object SelectedLevel
        {
            get => _selectedLevel;
            set { if (SetProperty(ref _selectedLevel, value)) ApplyFiltersAndUpdateView(); }
        }

        private ObservableCollection<SpaceTypeTreeNodeViewModel> _spaceTypeTreeNodes = new ObservableCollection<SpaceTypeTreeNodeViewModel>();
        public ObservableCollection<SpaceTypeTreeNodeViewModel> SpaceTypeTreeNodes { get => _spaceTypeTreeNodes; private set => SetProperty(ref _spaceTypeTreeNodes, value); }

        private SpaceTypeTreeNodeViewModel _selectedSpaceTypeNode;
        public SpaceTypeTreeNodeViewModel SelectedSpaceTypeNode { get => _selectedSpaceTypeNode; set { if (SetProperty(ref _selectedSpaceTypeNode, value)) UpdateSpaceList(); } }

        private ObservableCollection<SpaceDisplayViewModel> _filteredSpaceList = new ObservableCollection<SpaceDisplayViewModel>();
        public ObservableCollection<SpaceDisplayViewModel> FilteredSpaceList
        {
            get => _filteredSpaceList;
            private set
            {
                if (SetProperty(ref _filteredSpaceList, value))
                {
                    OnPropertyChanged(nameof(FilteredSpaceCount)); // 触发 Count 更新
                }
            }
        }
        public int FilteredSpaceCount => FilteredSpaceList?.Count ?? 0;

        private string _spaceFilterText = string.Empty;
        public string SpaceFilterText
        {
            get => _spaceFilterText;
            set { if (SetProperty(ref _spaceFilterText, value)) UpdateSpaceList(); }
        }

        // --- 命令 ---
        public IAsyncRelayCommand LoadSpaceDataCommand { get; }
        public IRelayCommand<object> UpdateSelectedSpaceTypeNodeCommand { get; }
        // 注意：命令参数改为 List<SpaceDisplayViewModel>，以便从 View 传递选中的项
        public IRelayCommand<List<SpaceDisplayViewModel>> ChangeSpaceTypeCommand { get; }
        // ... 其他命令 (Add, Rename, Delete)
        public IRelayCommand RequestConfirmAndAssignSpaceTypeCommand { get; }
        // --- 构造函数 ---
        public SpaceTabViewModel(Document doc, UIApplication uiApp)
        {
            _doc = doc ?? throw new ArgumentNullException(nameof(doc));
            _uiApp = uiApp ?? throw new ArgumentNullException(nameof(uiApp));
            _uiDoc = new UIDocument(doc);

            // 获取配置文件路径
            try
            {
                string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                // 确保这里的公司/产品名与 SpaceManagerVm 一致，以便共享同一个文件
                string configDir = Path.Combine(appDataPath, "维树科技", "空间管理器");
                Directory.CreateDirectory(configDir);
                _configFilePath = Path.Combine(configDir, "SpaceTypeCategory.json"); // 使用相同的 JSON 文件名
                Debug.WriteLine($"SpaceTabViewModel: 配置文件路径设置为: {_configFilePath}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"SpaceTabViewModel 错误：无法设置配置文件路径 - {ex.Message}");
                _configFilePath = "SpaceTypeCategory.json"; // 降级
            }

            LoadSpaceDataCommand = new AsyncRelayCommand(LoadInitialSpaceDataAsync);
            UpdateSelectedSpaceTypeNodeCommand = new RelayCommand<object>(selectedItem => { SelectedSpaceTypeNode = selectedItem as SpaceTypeTreeNodeViewModel; });
            // **修正 ChangeSpaceTypeCommand 初始化和 CanExecute/Execute 签名**
            ChangeSpaceTypeCommand = new RelayCommand<List<SpaceDisplayViewModel>>(ExecuteChangeSpaceType, CanExecuteChangeSpaceType);
            RequestConfirmAndAssignSpaceTypeCommand = new RelayCommand(ExecuteConfirmAndAssignSpaceType, CanExecuteConfirmAndAssignSpaceType);
        }



        // 新命令的 CanExecute (仅当暂存信息有效时)
        private bool CanExecuteConfirmAndAssignSpaceType()
        {
            return _dropTargetNode != null && _droppedSpaceVMs != null && _droppedSpaceVMs.Any();
        }

        //设置拖放信息
        internal void StoreDropInfo(SpaceTypeTreeNodeViewModel targetNode, List<SpaceDisplayViewModel> droppedVms)
        {
            _dropTargetNode = targetNode;
            _droppedSpaceVMs = droppedVms;
            (RequestConfirmAndAssignSpaceTypeCommand as RelayCommand)?.NotifyCanExecuteChanged();
            Debug.WriteLine($"StoreDropInfo: Stored target '{_dropTargetNode?.DisplayName}' and {_droppedSpaceVMs?.Count ?? 0} VMs.");
        }

        // 清理拖放信息 
        internal void ClearDropInfo() 
        {
            _dropTargetNode = null;
            _droppedSpaceVMs = null;
            (RequestConfirmAndAssignSpaceTypeCommand as RelayCommand)?.NotifyCanExecuteChanged();
            Debug.WriteLine("ClearDropInfo: Cleared stored drop info.");
        }

        // 新命令的 Execute
        private void ExecuteConfirmAndAssignSpaceType()
        {
            // 从暂存字段获取信息
            var targetNode = _dropTargetNode;
            var selectedVMs = _droppedSpaceVMs; // 这是最初拖动的 VM 列表

            // 清空暂存信息
            ClearDropInfo(); // 调用清理方法

            if (targetNode == null || !targetNode.IsLeafNode || targetNode.AssociatedSpaceTypeId == ElementId.InvalidElementId) { /* ... 错误处理 ... */ return; }
            if (selectedVMs == null || !selectedVMs.Any()) { /* ... 错误处理 ... */ return; }

            ElementId newTypeId = targetNode.AssociatedSpaceTypeId;
            string newTypeName = targetNode.DisplayName;
            //  不再需要 selectedSpaceIds 列表，因为我们直接处理 VM   
            // List<ElementId> selectedSpaceIds = selectedVMs.Select(vm => vm.RevitSpace.Id).ToList();
            int count = selectedVMs.Count;

            // 2. 用户确认 (保持不变)
            var confirmResult = MessageBox.Show($"确定要将选中的 {count} 个空间的类型更改为“{newTypeName}”吗？", "确认归类", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (confirmResult != MessageBoxResult.Yes) return;

            // 3. 执行 Revit 操作
            bool transactionSuccess = false; //  用于标记事务是否成功提交   
            int successCountInternal = 0; // 用于统计内部成功数
            int skippedCountInternal = 0;
            int failCountInternal = 0;
            string transactionResultMessage = ""; // 用于存储事务结果消息

            RevitContextHelper.Execute(uiApp =>
            {
                Document localDoc = uiApp.ActiveUIDocument.Document;
                using (Transaction tx = new Transaction(localDoc, $"更改空间类型为 {newTypeName} (Drop)"))
                {
                    try
                    {
                        tx.Start();
                        foreach (var vm in selectedVMs) // 仍然需要遍历来执行修改
                        {
                            Space space = localDoc.GetElement(vm.RevitSpace.Id) as Space;
                            if (space != null && space.IsValidObject)
                            {
                                if (space.SpaceTypeId == newTypeId) { skippedCountInternal++; continue; }
                                try
                                {
                                    space.SpaceTypeId = newTypeId;
                                    successCountInternal++;
                                    //  不再在这里填充 successfullyUpdatedVMs   
                                }
                                catch (Exception changeEx) { Debug.WriteLine($"更改空间 {vm.RevitSpace.Id} 类型失败: {changeEx.Message}"); failCountInternal++; }
                            }
                            else { Debug.WriteLine($"跳过无效的空间 ID: {vm.RevitSpace.Id}"); failCountInternal++; }
                        }

                        if (successCountInternal > 0)
                        {
                            tx.Commit();
                            Debug.WriteLine($"Transaction Committed Successfully for {successCountInternal} spaces (Drop).");
                            transactionSuccess = true; //  标记事务成功   
                        }
                        else
                        {
                            tx.RollBack();
                            Debug.WriteLine($"Transaction Rolled Back for {successCountInternal} spaces (Drop).");
                            transactionSuccess = false; // 标记事务失败或无操作
                        }

                        // 准备结果消息
                        string summary = $"拖放归类完成。\n成功更改: {successCountInternal}\n";
                        if (skippedCountInternal > 0) summary += $"跳过: {skippedCountInternal}\n";
                        if (failCountInternal > 0) summary += $"失败/无效: {failCountInternal}\n";
                        transactionResultMessage = summary.Trim();
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"!!!!!! EXCEPTION during Revit Transaction: {ex.ToString()}");
                        if (tx.HasStarted() && tx.GetStatus() == TransactionStatus.Started) tx.RollBack();
                        transactionResultMessage = $"拖放归类时发生错误: {ex.Message}";
                        transactionSuccess = false; // 标记事务失败
                    }
                } // using Transaction

                //  在 Revit 上下文的末尾显示 TaskDialog 
                TaskDialog.Show("归类结果", transactionResultMessage);

            }); // RevitContextHelper.Execute

            // 4. 在 UI 线程执行局部刷新 (在 RevitContextHelper 调用之后 )
            Application.Current?.Dispatcher?.Invoke(() => {
                //  根据事务成功状态决定是否刷新以及更新哪些 ViewModel   
                if (transactionSuccess && successCountInternal > 0) // 必须事务成功且确实有更改
                {
                    Debug.WriteLine($"Requesting local UI refresh because transaction was successful for {successCountInternal} items.");

                   
                    foreach (var updatedVm in selectedVMs) // 假设都需要更新（如果需要更精确，需要传递失败/跳过的ID回来）
                    {
                       
                        updatedVm.SpaceTypeName = newTypeName; // 更新 ViewModel
                    }
                    RefreshTreeCountsAndMoveItems(selectedVMs, targetNode); // 传递原始列表和目标节点
                    
                }
                else
                {
                    Debug.WriteLine($"Skipping local UI refresh because transactionSuccess={transactionSuccess} or successCountInternal={successCountInternal}.");
                }
            });
        }

        // --- Command Implementations ---
        private async Task LoadInitialSpaceDataAsync()
        {
           
            Debug.WriteLine("开始加载 Space 数据...");
            List<Level> actualLevels = null;
            List<Space> allSpaces = null;
            List<HVACLoadSpaceType> allSpaceTypes = null;

            try
            {
                await Task.Run(() =>
                {
                    try { 
                        actualLevels = new FilteredElementCollector(_doc).OfClass(typeof(Level)).Cast<Level>().OrderBy(o => o.Elevation).ToList(); 
                    } 
                    catch { 
                        actualLevels = new List<Level>(); 
                    }

                    try {
                        allSpaces = GetAllSpaces(_doc); 
                    } catch { 
                        allSpaces = new List<Space>();
                    }

                    try { 
                        allSpaceTypes = GetAllSpaceTypes(_doc);
                        Debug.WriteLine($"GetAllSpaceTypes 返回了 {allSpaceTypes?.Count ?? -1} 个类型。");
                    } catch (Exception ex)
                    {
                        Debug.WriteLine($"!!!!!! 调用 GetAllSpaceTypes 时捕获到异常: {ex.ToString()}");
                        allSpaceTypes = new List<HVACLoadSpaceType>();
                    }
                });

                var levelsForComboBox = new List<object>();
                levelsForComboBox.Add(AllLevelsPlaceholder);
                levelsForComboBox.AddRange(actualLevels ?? new List<Level>());
                Levels = levelsForComboBox;
                _allSpaces = allSpaces ?? new List<Space>();

                SelectedLevel = AllLevelsPlaceholder;
                

                Debug.WriteLine("Space 数据加载完成 (SpaceTabViewModel)。");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"加载 Space 数据过程中发生严重错误 (SpaceTabViewModel): {ex.Message}");
                MessageBox.Show($"加载空间数据时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void ApplyFiltersAndUpdateView()
        {

            Debug.WriteLine($"应用筛选 (Space)。当前选中楼层: {GetSelectedLevelName()}");
            List<Space> spacesToProcess;
            Level currentSelectedLevel = SelectedLevel as Level;

            if (SelectedLevel == AllLevelsPlaceholder || currentSelectedLevel == null)
            { spacesToProcess = _allSpaces; }
            else
            {
                const double elevationTolerance = 0.001;
                spacesToProcess = _allSpaces.Where(s => { Level spaceLevel = null; try { if (s.LevelId != null && s.LevelId != ElementId.InvalidElementId) spaceLevel = s.Document.GetElement(s.LevelId) as Level; } catch { } return spaceLevel != null && Math.Abs(spaceLevel.Elevation - currentSelectedLevel.Elevation) < elevationTolerance; }).ToList();
            }
            Debug.WriteLine($"楼层筛选后，待处理空间数: {spacesToProcess.Count}");


            // *** 获取所有 Space Types ***
            List<HVACLoadSpaceType> allValidSpaceTypes = null;
            try
            {
               
                await Task.Run(() => { // 确保在后台线程
                    allValidSpaceTypes = GetAllSpaceTypes(_doc);
                });
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"获取空间类型出错: {ex.Message}");
                allValidSpaceTypes = new List<HVACLoadSpaceType>();
            }
 



            //调用新的 BuildSpaceTypeTree 
            BuildSpaceTypeTree(allValidSpaceTypes, spacesToProcess);
   

            //清理json
            await CleanUpJsonBasedOnInvalidNodesAsync();

            // 更新列表 (Select first node if none selected)
            if (SelectedSpaceTypeNode == null && SpaceTypeTreeNodes.Any())
            {
                SelectedSpaceTypeNode = SpaceTypeTreeNodes.FirstOrDefault();
            }
            else
            {
                UpdateSpaceList(); // 确保在已有选中节点时列表也更新
            }
        }

        private void BuildSpaceTypeTree(List<HVACLoadSpaceType> actualSpaceTypesList, List<Space> spaces)
        {
            Debug.WriteLine($"开始 BuildSpaceTypeTree (JSON结构)。实际类型数: {actualSpaceTypesList?.Count ?? 0}，待处理空间数: {spaces?.Count ?? 0}。");

            // --- 1. 加载 JSON 配置 ---
            var categoryConfig = LoadCategoryConfiguration(); // 使用下面的辅助方法

            // --- 2. 从 JSON 配置递归构建 ViewModel 树结构 (只建骨架) ---
            var userDefinedRootNodes = new ObservableCollection<SpaceTypeTreeNodeViewModel>();
            BuildVmTreeRecursive(categoryConfig ?? new List<CategoryNodeConfig>(), null, userDefinedRootNodes);
            Debug.WriteLine($"从配置构建了 {userDefinedRootNodes.Count} 个用户定义的根 ViewModel 节点。");

            // --- 3. 获取实际 SpaceType 并创建查找字典 ---
            var actualSpaceTypesDict = actualSpaceTypesList?
                .Where(t => t != null && t.IsValidObject)
                .ToDictionary(t => t.Name, t => t, StringComparer.OrdinalIgnoreCase)
                ?? new Dictionary<string, HVACLoadSpaceType>(StringComparer.OrdinalIgnoreCase);
            Debug.WriteLine($"创建了包含 {actualSpaceTypesDict.Count} 个实际 SpaceType 的查找字典。");

            // --- 4. 映射实际类型 ID 到 ViewModel 树的叶节点 ---
            MapSpaceTypeIdsToLeafNodes(userDefinedRootNodes, actualSpaceTypesDict); // 使用下面的辅助方法

            // --- 5. 查找或创建 "其他类" 节点 (用于未匹配的空间) ---
            const string defaultCategoryNodeName = "其他类"; // 与 JSON 中的名称对应
            var defaultNode = FindNodeByNameRecursive(userDefinedRootNodes, defaultCategoryNodeName);
            if (defaultNode == null)
            {
                Debug.WriteLine($"警告：在 JSON 配置树中未找到 '{defaultCategoryNodeName}' 节点。将创建一个临时的，但空间可能无法正确归类。");
                // 最好确保 JSON 文件包含 "其他类"
                defaultNode = new SpaceTypeTreeNodeViewModel(defaultCategoryNodeName, ElementId.InvalidElementId, null);
                // 不将其添加到 userDefinedRootNodes，因为它不是 JSON 定义的顶级
            }
            else
            {
                Debug.WriteLine($"找到了 '{defaultCategoryNodeName}' 节点用于处理未匹配的空间。");
            }
            // 如果 defaultNode 仍然为 null，后续会有问题，需要健壮处理

            // --- 6. 创建 "全部空间" 节点 ---
            const string allSpacesNodeName = "全部空间";
            var allSpacesNode = new SpaceTypeTreeNodeViewModel(allSpacesNodeName, ElementId.InvalidElementId, null)
            {
                IsAllSpacesNode = true,
                IsExpanded = true
            };

            // --- 7. 关联 Space 到具体节点 ---
            ClearAllSpaceAssociations(userDefinedRootNodes); // 清空用户树的旧关联
            allSpacesNode.AssociatedSpaces.Clear();
            if (defaultNode != null) defaultNode.AssociatedSpaces.Clear();

            var safeSpaces = spaces ?? new List<Space>();
            foreach (var space in safeSpaces)
            {
                SpaceTypeTreeNodeViewModel targetNode = defaultNode; // 默认归到 "其他类" 或未找到的节点
                if (space.SpaceTypeId != null && space.SpaceTypeId != ElementId.InvalidElementId)
                {
                    // 在 userDefinedRootNodes 及其子孙中查找 TypeId 匹配的节点
                    var foundNode = FindNodeByTypeIdRecursive(userDefinedRootNodes, space.SpaceTypeId);
                    if (foundNode != null)
                    {
                        targetNode = foundNode; // 找到了匹配的叶节点
                    }
                    // 如果没找到 (foundNode == null)，则保持 targetNode 为 defaultNode
                }

                // 添加到目标节点 (进行 null 检查)
                if (targetNode != null && !targetNode.AssociatedSpaces.Contains(space))
                {
                    targetNode.AssociatedSpaces.Add(space);
                }
                else if (targetNode == null)
                {
                    Debug.WriteLine($"错误：无法关联空间 {space.Id}，因为目标节点（包括默认节点）为 null！");
                }
            }
            // 将筛选后的所有空间关联到 "全部空间" 节点
            allSpacesNode.AssociatedSpaces.AddRange(safeSpaces);

            // --- 8. 构建最终 TreeView 结构 ---
            allSpacesNode.Children.Clear();
            foreach (var userRoot in userDefinedRootNodes)
            {
                allSpacesNode.Children.Add(userRoot);
                userRoot.Parent = allSpacesNode; // 设置父级
            }
            // 根据需要决定是否添加 defaultNode 到根的 Children
            // 如果 JSON 中定义了 "其他类" 作为顶级，它会通过上面的循环添加
            // 如果是临时创建的，看是否要显示出来
            // if (defaultNode != null && defaultNode.Parent == null) // 如果是临时创建的，可能要加
            // {
            //    allSpacesNode.Children.Add(defaultNode);
            //    defaultNode.Parent = allSpacesNode;
            // }

            var finalRootNodes = new ObservableCollection<SpaceTypeTreeNodeViewModel> { allSpacesNode };

            // --- 9. 更新计数和 UI ---
            UpdateAllNodeCounts(finalRootNodes); // 使用现有辅助方法
            SpaceTypeTreeNodes = finalRootNodes; // 更新绑定的属性
            Debug.WriteLine($"SpaceType 树 (JSON 结构) 构建完成。根节点下子节点数: {allSpacesNode.Children.Count}");

            //   添加日志：构建完成后检查叶节点 ID    
            Debug.WriteLine("--- Final Tree Structure - Leaf Node Check ---");
            Action<SpaceTypeTreeNodeViewModel, string> printNodeInfo = null;
            printNodeInfo = (node, indent) => {
                if (node.IsLeafNode)
                {
                    Debug.WriteLine($"{indent}Leaf: '{node.DisplayName}', Mapped TypeId: {node.AssociatedSpaceTypeId}");
                }
                else
                {
                    Debug.WriteLine($"{indent}Node: '{node.DisplayName}'");
                    foreach (var child in node.Children)
                    {
                        printNodeInfo(child, indent + "  ");
                    }
                }
            };
            foreach (var root in finalRootNodes) // finalRootNodes 是最终构建的树根集合
            {
                printNodeInfo(root, "");
            }
            Debug.WriteLine("--- End of Leaf Node Check ---");

            Debug.WriteLine($"SpaceType 树 (JSON 结构) 构建完成。根节点下子节点数: {allSpacesNode.Children.Count}");
        }


        // --- 新增: 清理 JSON 中无效节点的方法 ---
        private async Task CleanUpJsonBasedOnInvalidNodesAsync()
        {
            Debug.WriteLine("开始检查并清理 JSON 中的无效节点...");
            var invalidNodePaths = new List<string>();
            FindInvalidLeafNodePathsRecursive(SpaceTypeTreeNodes, invalidNodePaths);

            if (invalidNodePaths.Any())
            {
                Debug.WriteLine($"发现 {invalidNodePaths.Count} 个无效节点路径需要从 JSON 中移除: {string.Join("; ", invalidNodePaths)}");
                try
                {
                    // 调用 Service 方法执行清理
                    bool cleanupSuccess = await SpaceTypeService.RemoveInvalidNodesFromJsonAsync(invalidNodePaths); // <-- 调用 Service

                    if (cleanupSuccess) { Debug.WriteLine("JSON 清理操作已成功调度执行。"); }
                    else { Debug.WriteLine("JSON 清理操作执行失败或部分失败。"); }
                }
                catch (Exception ex) { Debug.WriteLine($"!!!!!! 调用 JSON 清理服务时异常: {ex.ToString()}"); }
            }
            else { Debug.WriteLine("未在当前加载的树中发现无效节点路径，无需清理 JSON。"); }
        }

        // ****** 新增: 递归查找无效叶节点路径的辅助方法 ******
        private void FindInvalidLeafNodePathsRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, List<string> invalidPaths)
        {
            if (nodes == null) return;
            foreach (var node in nodes)
            {
                if (node.IsAllSpacesNode) { FindInvalidLeafNodePathsRecursive(node.Children, invalidPaths); continue; }
                if (node.IsLeafNode)
                {
                    if ((node.AssociatedSpaceTypeId == null || node.AssociatedSpaceTypeId == ElementId.InvalidElementId) && !node.DisplayName.Equals("其他类", StringComparison.OrdinalIgnoreCase))
                    {
                        string path = GetNodePath(node);
                        if (!string.IsNullOrEmpty(path)) { invalidPaths.Add(path); Debug.WriteLine($"  发现无效叶节点路径: {path} (节点名: {node.DisplayName})"); }
                        else { Debug.WriteLine($"  警告: 发现无效叶节点 '{node.DisplayName}'，但无法获取其路径。"); }
                    }
                }
                else { if (node.Children != null) { FindInvalidLeafNodePathsRecursive(node.Children, invalidPaths); } }
            }
        }

        // --- 确保 GetNodePath 方法存在且逻辑正确 ---
        private string GetNodePath(SpaceTypeTreeNodeViewModel node)
        {
            // 1. 处理无效输入或特殊节点
            if (node == null || node.IsAllSpacesNode || node.Parent == null)
            {
                // 如果节点无效，或者是根节点本身，或者没有父节点，则无法生成相对路径
                return null;
            }

            // 2. 如果父节点就是 "全部空间"，则路径就是节点自身的名字
            if (node.Parent.IsAllSpacesNode)
            {
                return node.DisplayName;
            }

            // 3. 对于嵌套节点，使用 Stack 收集路径段
            var pathSegments = new Stack<string>();
            var currentNode = node;

            // 向上遍历，直到父节点是 "全部空间" 节点
            int loopCounter = 0; // 添加循环计数器防止死循环
            while (currentNode != null && currentNode.Parent != null && !currentNode.Parent.IsAllSpacesNode && loopCounter < 100) // 增加循环次数限制
            {
                pathSegments.Push(currentNode.DisplayName); // 将当前节点名称压入栈
                currentNode = currentNode.Parent; // 移动到父节点
                loopCounter++;
            }

            // 检查循环是否因为达到限制而退出
            if (loopCounter >= 100)
            {
                Debug.WriteLine($"GetNodePath 错误: 路径过深或可能存在循环，起始节点: '{node.DisplayName}'");
                return null;
            }


            // 4. 将最后一个父节点（即 "全部空间" 的直接子节点）的名称也压入栈
            if (currentNode != null && currentNode.Parent != null && currentNode.Parent.IsAllSpacesNode)
            {
                pathSegments.Push(currentNode.DisplayName);
            }
            else
            {
                // 如果循环结束后未能正确追溯到根用户节点，说明树结构可能存在问题
                Debug.WriteLine($"GetNodePath 警告: 未能正确追溯到根用户节点，起始节点: '{node.DisplayName}'，最后节点: '{currentNode?.DisplayName}'，其父节点是否为根: {currentNode?.Parent?.IsAllSpacesNode}");
                return null; // 无法构建有效路径
            }

            // 5. 将栈中的路径段连接起来
            // Stack 是后进先出，所以直接 Join 会得到 "孙/子/父" 的顺序
            // 我们需要的是 "父/子/孙"，所以需要 Reverse()
            // return string.Join("/", pathSegments.Reverse()); // <-- 之前的错误在这里，Stack Join 不需要 Reverse

            // Stack<T> 直接 Join 的顺序就是 父/子/孙，因为最后压入的是最顶层的父节点
            return string.Join("/", pathSegments); // <-- 直接 Join 即可

        }

        // --- 辅助方法：加载 JSON 配置 (从 SpaceManagerVm 借鉴) ---
        private List<CategoryNodeConfig> LoadCategoryConfiguration()
        {
            Debug.WriteLine($"SpaceTabViewModel: 从 '{_configFilePath}' 加载分类配置。");
            try
            {
                if (File.Exists(_configFilePath))
                {
                    string json = File.ReadAllText(_configFilePath);
                    // JSON 根是一个包含单个对象的数组，该对象代表 "全部房间"
                    var rootConfigList = JsonConvert.DeserializeObject<List<CategoryNodeConfig>>(json);
                    // 我们需要的是 "全部房间" 下面的子节点
                    if (rootConfigList != null && rootConfigList.Count > 0)
                    {
                        Debug.WriteLine($"JSON 配置加载成功。返回 '{rootConfigList[0].Name}' 的子节点。");
                        return rootConfigList[0].Children ?? new List<CategoryNodeConfig>();
                    }
                    else
                    {
                        Debug.WriteLine("警告：JSON 文件为空或结构不正确（期望根数组包含一个对象）。返回空配置。");
                        return new List<CategoryNodeConfig>();
                    }
                }
                else
                {
                    Debug.WriteLine("警告：配置文件不存在。返回空配置。");
                    // 可以考虑返回一个默认结构或提示用户
                    return new List<CategoryNodeConfig>(); // 返回空列表
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"加载配置错误: {ex.Message}");
                MessageBox.Show($"无法加载分类配置文件: {ex.Message}\n将使用空结构。", "配置错误", MessageBoxButton.OK, MessageBoxImage.Warning);
                return new List<CategoryNodeConfig>(); // 出错时返回空列表
            }
        }

        // --- 辅助方法：递归构建 ViewModel 树 (从 SpaceManagerVm 借鉴) ---
        private void BuildVmTreeRecursive(List<CategoryNodeConfig> configNodes, SpaceTypeTreeNodeViewModel parentVmNode, ObservableCollection<SpaceTypeTreeNodeViewModel> vmNodeCollection)
        {
            if (configNodes == null) return;
            foreach (var configNode in configNodes)
            {
                if (string.IsNullOrWhiteSpace(configNode.Name)) continue;
                var newVmNode = new SpaceTypeTreeNodeViewModel(configNode.Name, ElementId.InvalidElementId, parentVmNode);

                // 如果是顶级节点 (parentVmNode 为 null)，则默认展开
                if (parentVmNode == null)
                {
                    newVmNode.IsExpanded = true;
                }
                // 或者展开前两级
                // if (parentVmNode == null || parentVmNode.Parent == null) // 假设根的 Parent 是 null
                // {
                //     newVmNode.IsExpanded = true;
                // }

                vmNodeCollection.Add(newVmNode);
                if (configNode.Children != null && configNode.Children.Any())
                {
                    BuildVmTreeRecursive(configNode.Children, newVmNode, newVmNode.Children);
                }
            }
        }

        // --- 辅助方法：递归映射 ID 到叶节点 (与硬编码版本类似) ---
        private void MapSpaceTypeIdsToLeafNodes(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, Dictionary<string, HVACLoadSpaceType> actualTypes)
        {
            if (nodes == null) return;
            //   添加日志：打印传入的字典内容 (只打印少量键值对示例)    
            Debug.WriteLine($"--- MapSpaceTypeIdsToLeafNodes called. Actual types dictionary (sample keys): {string.Join(", ", actualTypes.Keys.Take(5))} ---");

            foreach (var node in nodes)
            {
                if (node.IsLeafNode) // 只处理叶节点
                {
                    //   添加日志：打印正在处理的叶节点名称    
                    Debug.WriteLine($"  Processing Leaf Node for Mapping: '{node.DisplayName}'");
                    if (actualTypes.TryGetValue(node.DisplayName, out var spaceType))
                    {
                        node.AssociatedSpaceTypeId = spaceType.Id;
                        //   添加日志：打印映射成功信息    
                        Debug.WriteLine($"    -> SUCCESS: Mapped '{node.DisplayName}' to TypeId {spaceType.Id}");
                    }
                    else
                    {
                        //   添加日志：打印映射失败信息    
                        Debug.WriteLine($"    -> WARNING: Mapping FAILED for '{node.DisplayName}'. No matching key found in actualTypes dictionary.");
                        // 可以在这里打印 actualTypes.Keys 帮助对比
                        // Debug.WriteLine($"      Available keys in dictionary: {string.Join("; ", actualTypes.Keys)}");
                    }
                }
                else
                {
                    MapSpaceTypeIdsToLeafNodes(node.Children, actualTypes);
                }
            }
        }

        // --- 辅助方法：递归按名称查找节点 (从 SpaceManagerVm 借鉴) ---
        private SpaceTypeTreeNodeViewModel FindNodeByNameRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, string name)
        {
            if (nodes == null || string.IsNullOrWhiteSpace(name)) return null;
            foreach (var node in nodes)
            {
                if (node.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase)) return node;
                var foundInChildren = FindNodeByNameRecursive(node.Children, name);
                if (foundInChildren != null) return foundInChildren;
            }
            return null;
        }

        // --- 辅助方法：递归按 TypeId 查找节点 (与硬编码版本相同) ---
        private SpaceTypeTreeNodeViewModel FindNodeByTypeIdRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, ElementId typeId)
        {
            if (nodes == null || typeId == null || typeId == ElementId.InvalidElementId) return null;
            foreach (var node in nodes)
            {
                // 直接比较 ID
                if (node.AssociatedSpaceTypeId == typeId)
                {
                    return node;
                }
                // 递归查找子节点
                var foundInChildren = FindNodeByTypeIdRecursive(node.Children, typeId);
                if (foundInChildren != null)
                {
                    return foundInChildren;
                }
            }
            return null; // 未找到
        }

        // --- 辅助方法：递归清空关联 (与硬编码版本相同) ---
        private void ClearAllSpaceAssociations(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes)
        {
            if (nodes == null) return;
            foreach (var node in nodes)
            {
                node.AssociatedSpaces.Clear();
                if (node.Children != null) ClearAllSpaceAssociations(node.Children);
            }
        }

        // --- 辅助方法：更新计数 (保持不变) ---
        private void UpdateAllNodeCounts(ObservableCollection<SpaceTypeTreeNodeViewModel> rootNodes) { if (rootNodes == null) return; Debug.WriteLine("--- 开始更新 SpaceType 树节点计数 ---"); foreach (var rootNode in rootNodes) { UpdateNodeCountsRecursively(rootNode); } Debug.WriteLine("--- SpaceType 树节点计数更新完毕 ---"); }
        private int UpdateNodeCountsRecursively(SpaceTypeTreeNodeViewModel node) { if (node == null) return 0; int childrenCount = 0; if (node.Children != null) { foreach (var child in node.Children) { childrenCount += UpdateNodeCountsRecursively(child); } } int totalCount = node.IsAllSpacesNode ? node.AssociatedSpaces.Count : node.AssociatedSpaces.Count + childrenCount; node.TotalSpaceCount = totalCount; return totalCount; }


        // --- UpdateSpaceList (检查是否需要修改) ---
        // 这个方法应该可以保持不变，因为它依赖于 SelectedSpaceTypeNode 和 GetAllSpacesInChildren()，
        // 而 GetAllSpacesInChildren() 的逻辑在新节点结构下也是正确的。
        private void UpdateSpaceList()
        {
            Debug.WriteLine($"更新空间列表。分类: {SelectedSpaceTypeNode?.DisplayName ?? "无"}, 楼层: {GetSelectedLevelName()}, 筛选文本: '{SpaceFilterText}'");
            var newFilteredList = new ObservableCollection<SpaceDisplayViewModel>();
            List<Space> finalSpaces = new List<Space>();
            if (SelectedSpaceTypeNode != null)
            {
                // 使用 GetAllSpacesInChildren 获取当前节点及其子孙的所有关联空间
                List<Space> spacesToShow = SelectedSpaceTypeNode.GetAllSpacesInChildren();

                // 应用文本筛选 (保持不变)
                string filterText = SpaceFilterText?.Trim();
                if (string.IsNullOrWhiteSpace(filterText)) { finalSpaces = spacesToShow; }
                else { finalSpaces = spacesToShow.Where(s => s.Name != null && s.Name.IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0 || s.Number != null && s.Number.IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); }
            }
            else { Debug.WriteLine("未选中空间类型节点，空间列表为空。"); }

            Debug.WriteLine($"准备为 {finalSpaces.Count} 个空间创建 ViewModel。");
            foreach (var space in finalSpaces.OrderBy(s => s.Number)) { try { newFilteredList.Add(new SpaceDisplayViewModel(space, _doc)); } catch (Exception ex) { Debug.WriteLine($"为空间 {space.Id} 创建 SpaceDisplayViewModel 时出错: {ex.Message}"); } }

            FilteredSpaceList = newFilteredList; // 更新列表，触发 UI 变化
        }


        // --- ChangeSpaceTypeCommand (Execute 和 CanExecute) ---
        // 这部分逻辑需要调整，因为现在目标节点 (SelectedSpaceTypeNode) 必须是叶节点，
        // 并且我们需要使用目标节点的 AssociatedSpaceTypeId 来设置 Space.SpaceTypeId。
        private bool CanExecuteChangeSpaceType(List<SpaceDisplayViewModel> selectedVMs)
        {
            bool isLeaf = SelectedSpaceTypeNode?.IsLeafNode ?? false;
            bool hasValidId = SelectedSpaceTypeNode?.AssociatedSpaceTypeId != null && SelectedSpaceTypeNode.AssociatedSpaceTypeId != ElementId.InvalidElementId;
            //   添加日志    
            Debug.WriteLine($"CanExecuteChangeSpaceType called. Node: '{SelectedSpaceTypeNode?.DisplayName ?? "null"}', IsLeaf: {isLeaf}, HasValidId: {hasValidId}");
            return SelectedSpaceTypeNode != null && isLeaf && hasValidId;
        }

        private void ExecuteChangeSpaceType(List<SpaceDisplayViewModel> selectedVMs)
        {
            // 1. 获取目标类型节点和 ID (从属性获取，CanExecute已保证其有效性)
            var targetTypeNode = SelectedSpaceTypeNode;
            //  再次检查 targetTypeNode 有效性，防止竞态条件或逻辑错误   
            if (targetTypeNode == null || !targetTypeNode.IsLeafNode || targetTypeNode.AssociatedSpaceTypeId == ElementId.InvalidElementId)
            {
                Debug.WriteLine($"ExecuteChangeSpaceType 错误: 目标节点无效 (Name='{targetTypeNode?.DisplayName}', IsLeaf={targetTypeNode?.IsLeafNode}, TypeId={targetTypeNode?.AssociatedSpaceTypeId})");
                MessageBox.Show("无法执行归类操作，因为当前选中的目标分类节点无效。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            ElementId newTypeId = targetTypeNode.AssociatedSpaceTypeId;
            string newTypeName = targetTypeNode.DisplayName;

            // 2. 获取选中的空间 ID (参数传入)
            if (selectedVMs == null || !selectedVMs.Any())
            {
                // 如果允许命令在没有选中项时触发（例如按钮点击），这里需要提示
                // 对于拖放，这里理论上总有选中项
                Debug.WriteLine("ExecuteChangeSpaceType 警告: selectedVMs 为空或 null。");
                MessageBox.Show("没有接收到要归类的空间列表。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            List<ElementId> selectedSpaceIds = selectedVMs.Select(vm => vm.RevitSpace.Id).ToList();
            int count = selectedSpaceIds.Count;

            // 3. 用户确认 (保持不变)
            var confirmResult = MessageBox.Show($"确定要将选中的 {count} 个空间的类型更改为“{newTypeName}”吗？", "确认更改类型", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (confirmResult != MessageBoxResult.Yes) return;


            // 4. 执行 Revit 操作 (需要 RevitContextHelper，如果 SpaceTabViewModel 无法直接访问，需要传递或通过服务定位)
            // 假设可以通过某种方式调用 RevitContextHelper.Execute

            List<SpaceDisplayViewModel> successfullyUpdatedVMs = new List<SpaceDisplayViewModel>();
            bool operationCompleted = false;
            RevitContextHelper.Execute(uiApp => // 假设可以这样调用
            {
                Document localDoc = uiApp.ActiveUIDocument.Document;
                int successCount = 0;
                int failCount = 0;
                int skippedCount = 0;
                using (Transaction tx = new Transaction(localDoc, $"更改空间类型为 {newTypeName}"))
                {
                    try
                    {
                        tx.Start();
                        foreach (var vm in selectedVMs) //  遍历 ViewModel 列表   
                        {
                            Space space = localDoc.GetElement(vm.RevitSpace.Id) as Space;
                            if (space != null && space.IsValidObject)
                            {
                                if (space.SpaceTypeId == newTypeId) { skippedCount++; continue; }
                                try
                                {
                                    space.SpaceTypeId = newTypeId;
                                    successCount++;
                                    //  记录成功更新的 ViewModel   
                                    successfullyUpdatedVMs.Add(vm);
                                }
                                catch (Exception changeEx) { Debug.WriteLine($"更改空间 {vm.RevitSpace.Id} 类型失败: {changeEx.Message}"); failCount++; }
                            }
                            else { Debug.WriteLine($"跳过无效的空间 ID: {vm.RevitSpace.Id}"); failCount++; }
                        }


                        if (successCount > 0)
                        {
                            tx.Commit();
                            Debug.WriteLine($"Transaction Committed Successfully for {successCount} spaces.");

                            //  在事务提交后，直接更新这些 ViewModel 的属性   
                            // (注意：这里仍然在 Revit API 上下文中，但修改 ViewModel 属性通常是安全的，
                            // 因为它们最终通过 PropertyChanged 通知 UI 在 UI 线程更新)
                            foreach (var updatedVm in successfullyUpdatedVMs)
                            {
                                // 手动更新 ViewModel 的类型名称，避免等待完全刷新
                                updatedVm.SpaceTypeName = newTypeName;
                                Debug.WriteLine($"Updated ViewModel for Space {updatedVm.RevitSpace.Id} TypeName to '{newTypeName}'.");
                            }
                            //  ViewModel 更新结束   
                        }
                        else { tx.RollBack(); }

                        // 显示结果 (TaskDialog 更好)
                        string summary = $"成功更改 {successCount} 个空间的类型。\n";
                        if (skippedCount > 0) summary += $"跳过 {skippedCount} 个 (已是目标类型)。\n";
                        if (failCount > 0) summary += $"失败/无效 {failCount} 个。\n";
                        TaskDialog.Show("操作完成", summary.Trim());


                        operationCompleted = true; // 标记操作完成


                    }
                    catch (Exception ex) { Debug.WriteLine($"更改空间类型事务失败: {ex.Message}"); if (tx.HasStarted() && !tx.HasEnded()) tx.RollBack(); TaskDialog.Show("错误", $"更改空间类型时发生错误: {ex.Message}"); }
                }
            });



            // 5. 异步刷新 UI (如果 Revit 操作完成)
            // 需要访问主窗口 Dispatcher，或者在 SpaceTabViewModel 中持有 Dispatcher 引用

            //System.Windows.Application.Current?.Dispatcher?.InvokeAsync(async () => {
            //    if (operationCompleted) // 只在 Revit 操作完成后刷新
            //    {
            //        Debug.WriteLine("ChangeSpaceType complete. Requesting UI refresh...");
            //        await Task.Delay(250); // 短暂延迟
            //        // 重新执行筛选和树构建以更新界面
            //        ApplyFiltersAndUpdateView();
            //        Debug.WriteLine("UI refresh (ApplyFiltersAndUpdateView) completed.");


            //        var nodeToReselect = FindNodeByNameRecursive(this.SpaceTypeTreeNodes, newTypeName);
            //        if (nodeToReselect != null)
            //        {
            //            // 可能需要确保父节点展开
            //            ExpandParents(nodeToReselect); // 需要实现 ExpandParents
            //            nodeToReselect.IsSelected = true;
            //            this.SelectedSpaceTypeNode = nodeToReselect; // 直接设置可能不会触发 TreeView 的选中效果，IsSelected = true 通常更好
            //            Debug.WriteLine($"Reselected node '{newTypeName}' after refresh.");
            //        }

            //    }
            //});


            Application.Current?.Dispatcher?.Invoke(() => {
                if (successfullyUpdatedVMs.Any()) // 只有成功更新了才执行
                {
                    RefreshTreeCountsAndMoveItems(successfullyUpdatedVMs, targetTypeNode);
                }
            });


        }

        //  新增辅助方法：刷新树计数并将项移出当前列表 (如果当前选中节点不再包含它们)   
        private void RefreshTreeCountsAndMoveItems(List<SpaceDisplayViewModel> updatedVMs, SpaceTypeTreeNodeViewModel targetNode)
        {
            Debug.WriteLine($"RefreshTreeCountsAndMoveItems: Starting UI update for {updatedVMs.Count} items moved to '{targetNode.DisplayName}'...");

            // 1. 更新树节点计数 (需要找到旧节点和新节点)
            //    这需要更复杂的逻辑来找到旧节点并更新计数，或者直接调用 UpdateAllNodeCounts
            Debug.WriteLine("  Calling UpdateAllNodeCounts...");
            UpdateAllNodeCounts(SpaceTypeTreeNodes); // 简单起见，先完全重新计算计数
            Debug.WriteLine("  Tree counts updated.");

            // 2. 从当前 DataGrid 列表 (FilteredSpaceList) 中移除已移动的项
            Debug.WriteLine($"  Checking if current node ('{SelectedSpaceTypeNode?.DisplayName}') needs item removal (target is '{targetNode.DisplayName}')...");
            //    (如果当前选中的节点不是目标节点 targetNode)
            if (SelectedSpaceTypeNode != targetNode)
            {
                Debug.WriteLine($"    Current node is different. Preparing to remove items from FilteredSpaceList (current count: {FilteredSpaceList.Count})...");
                int removedCount = 0;
                for (int i = FilteredSpaceList.Count - 1; i >= 0; i--) // 从后往前删
                {
                    // 检查当前列表项是否在已更新的 VM 列表中
                    if (updatedVMs.Any(uvm => uvm.RevitSpace.Id == FilteredSpaceList[i].RevitSpace.Id))
                    {

                        FilteredSpaceList.RemoveAt(i);
                        removedCount++;
                    }
                }
                Debug.WriteLine($"  Removed {removedCount} items from the current DataGrid list ('{SelectedSpaceTypeNode?.DisplayName}').");
            }
            else
            {
                Debug.WriteLine($"  Current node ('{SelectedSpaceTypeNode?.DisplayName}') is the target node. Items remain in the list.");
                // 如果当前就是目标节点，可能需要刷新这些项的显示（虽然类型已在VM内部更新）
                // 可以通过重新设置 ItemsSource 或其他方式强制刷新，但通常 PropertyChanged 就够了
            }

            // 3. 触发 FilteredSpaceCount 的更新 (如果移除操作没有自动触发)
            OnPropertyChanged(nameof(FilteredSpaceCount));

            Debug.WriteLine($"RefreshTreeCountsAndMoveItems: UI update complete.");


        }

        // ExpandParents 辅助方法 (如果需要恢复选中)
        private void ExpandParents(SpaceTypeTreeNodeViewModel node)
        {
            if (node?.Parent != null)
            {
                if (!node.Parent.IsExpanded)
                {
                    node.Parent.IsExpanded = true;
                }
                ExpandParents(node.Parent);
            }
        }

        // --- 辅助方法 ---
        private List<Space> GetAllSpaces(Document doc) { 
            return new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_MEPSpaces).WhereElementIsNotElementType().Cast<Space>().Where(s => s != null && s.Area > 0 && s.Location != null).ToList(); }
        private List<HVACLoadSpaceType> GetAllSpaceTypes(Document doc)
        {
            if (doc == null || !doc.IsValidObject)
            {
                Debug.WriteLine("GetAllSpaceTypes 错误: 文档对象无效。");
                return new List<HVACLoadSpaceType>();
            }
            List<HVACLoadSpaceType> types = new List<HVACLoadSpaceType>();
            try
            {
                types = new FilteredElementCollector(doc)
                   .OfClass(typeof(HVACLoadSpaceType))
                   .Cast<HVACLoadSpaceType>()
                   .ToList();
                //   添加日志：打印获取到的类型名称    
                Debug.WriteLine($"--- Retrieved HVACLoadSpaceTypes from Revit (Count: {types.Count}) ---");
                foreach (var t in types)
                {
                    Debug.WriteLine($"    - Name: '{t.Name}', ID: {t.Id}");
                }
                Debug.WriteLine($"--- End of Retrieved HVACLoadSpaceTypes ---");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"获取 HVACLoadSpaceType 时出错: {ex.Message}");
                return new List<HVACLoadSpaceType>();
            }
            return types;


        }
        private void ClearSpaceTypeAssociations(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes) { if (nodes == null) return; foreach (var node in nodes) { node.AssociatedSpaces.Clear(); if (node.Children != null && node.Children.Any()) { ClearSpaceTypeAssociations(node.Children); } } }
        private string GetSelectedLevelName() { if (SelectedLevel == AllLevelsPlaceholder) return "(全部楼层)"; if (SelectedLevel is Level level) return level.Name; return "未知"; }

    }
}